package com.zjut.an.encrypt.interceptor;

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zjut.an.encrypt.annotation.EncryptField;
import com.zjut.an.encrypt.annotation.EncryptTable;
import com.zjut.an.util.tools.AESUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class EncryptInterceptor extends EmptyInterceptor{
	private final static Logger LOGGER = LoggerFactory.getLogger(EncryptInterceptor.class);

    /**
     * 更新时调用
     *
     * @param entity        实体类
     * @param id            主键
     * @param currentState  当前实体类对应的值
     * @param previousState 修改前实体类对应的值
     * @param propertyNames 字段名
     * @param types         实体类每个属性类型对应hibernate的类型
     * @return true | false true才会修改数据
     */
    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
        Object[] newState = dealField(entity, currentState, propertyNames, "onFlushDirty");
        return super.onFlushDirty(entity, id, newState, previousState, propertyNames, types);
    }

    /**
     * 加载时调用
     *
     * @param entity        实体类
     * @param id            主键
     * @param state         实体类对应的值
     * @param propertyNames 字段名
     * @param types         实体类每个属性类型对应hibernate的类型
     * @return true | false true才会修改数据
     */
    @Override
    public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        Object[] newState = dealField(entity, state, propertyNames, "onLoad");
        return super.onLoad(entity, id, newState, propertyNames, types);
    }

    /**
     * 保存时调用
     *
     * @param entity        实体类
     * @param id            主键
     * @param state         实体类对应的值
     * @param propertyNames 字段名
     * @param types         实体类每个属性类型对应hibernate的类型
     * @return true | false true才会修改数据
     */
    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        Object[] newState = dealField(entity, state, propertyNames, "onSave");
        return super.onSave(entity, id, newState, propertyNames, types);
    }


    /**
     * 处理字段对应的数据
     *
     * @param entity        实体类
     * @param state         数据
     * @param propertyNames 字段名称
     * @return 解密后的字段名称
     */
    private Object[] dealField(Object entity, Object[] state, String[] propertyNames, String type) {
        List<String> annotationFields = getAnnotationField(entity);
        LOGGER.info("调用方法：{}, 需要加密的字段：{}", type, annotationFields);
        // 遍历字段名和加解密字段名
        for (String aField : annotationFields) {
            for (int i = 0; i < propertyNames.length; i++) {
                if (!propertyNames[i].equals(aField)) {
                    continue;
                }
                // 如果字段名和加解密字段名对应且不为null或空
                if (state[i] == null || Objects.equals(state[i].toString(), "")) {
                    continue;
                }
                
                if ("onSave".equals(type) || "onFlushDirty".equals(type)) {
                    LOGGER.info("当前字段：{}, 加密前：{}", aField, state[i]);
                    state[i] = AESUtils.AESEncryptDemo(state[i].toString()); 
                    LOGGER.info("当前字段：{}, 加密后：{}", aField, state[i]);
                } else if ("onLoad".equals(type)) {
                    LOGGER.info("当前字段：{}, 解密前：{}", aField, state[i]);
                    state[i] = AESUtils.AESDecryptDemo(state[i].toString());
                    LOGGER.info("当前字段：{}, 解密后：{}", aField, state[i]);
                }
            }
        }
        return state;
    }


    /**
     * 获取实体类中带有注解EncryptField的变量名
     *
     * @param entity 实体类
     * @return 需要加解密的字段
     */
    private List<String> getAnnotationField(Object entity) {
        // 判断当前实体类是否有加解密注解
        Class<?> entityClass = entity.getClass();
        if (!entityClass.isAnnotationPresent(EncryptTable.class)) {
            return Collections.emptyList();
        }
        List<String> fields = new ArrayList<String>();
        // 获取实体类下的所有成员并判断是否存在加解密注解
        Field[] declaredFields = entityClass.getDeclaredFields();
        for (Field field : declaredFields) {
            EncryptField encryptField = field.getAnnotation(EncryptField.class);
            if (Objects.isNull(encryptField)) {
                continue;
            }
            fields.add(field.getName());
        }
        return fields;
    }
}
